(function (factory) {
  if (typeof define === 'function') {
    // 如果define已被定义，模块化代码
    define(function (require, exports, moudles) {
      var $ = require('jquery');
      require('upload');
      require('upload-css');

      factory($); // 初始化插件

      return jQuery; // 返回jQuery
    });
  } else {
    // 如果define没有被定义，正常执行jQuery
    factory(jQuery);
  }
}(function ($) {

  "use strict";

  var noop = $.noop;

  var sprintf = function (str) {
    var args = arguments,
      flag = true,
      i = 1;

    str = str.replace(/%s/g, function () {
      var arg = args[i++];

      if (typeof arg === 'undefined') {
        flag = false;
        return '';
      }
      return arg;
    });
    return flag ? str : '';
  };

  var allowedMethods = ['reset', 'getUploader'];

  $.fn.phxUploader = function (option) {
    var value, args = Array.prototype.slice.call(arguments, 1);

    this.each(function () {
      var $this = $(this),
        data = $this.data('phx-uploader');

      if (typeof option === 'string') {
        if (!data) return;

        if ($.inArray(option, allowedMethods) > 0) {
          value = data[option].apply(data, args);
        } else if (option === 'destroy') {
          $this.removeData('phx-uploader');
        }
      }

      if (!data) $this.data('phx-uploader', (data = new PhxUploader(this, option)));
    });

    return typeof value === 'undefined' ? this : value;
  };

  $.fn.phxUploader.fileTypes = {
    'excel': {
      extensions: 'xls,xlsx',
      mimeTypes: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    },
    'image': {
      extensions: 'gif,jpg,jpeg,bmp,png',
      mimeTypes: 'image/*'
    },
    'ppt': {
      extensions: 'ppt,pptx',
      mimeTypes: 'application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation'
    },
    'pdf': {
      extensions: 'pdf',
      mimeTypes: 'application/pdf'
    },
    'word': {
      extensions: 'doc,docx,txt',
      mimeTypes: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    },
    'xml': {
      extensions: 'xml',
      mimeTypes: 'application/xml'
    },
    'png': {
      extensions: 'png',
      mimeTypes: 'image/png'
    },
    'jpg': {
      extensions: 'jpg',
      mimeTypes: 'image/jpeg'
    },
    'gif': {
      extensions: 'gif',
      mimeTypes: 'image/gif'
    },
    'report': {
      extensions: 'jrxml',
      mimeTypes: ''
    }
  };

  $.fn.phxUploader.defaults = {
    pick: {
      id: '.uploader-file-picker',
      label: '点击选择文件'
    },
    dnd: '.uploader-dnd-area',
    paste: document.body,
    swf: 'Uploader.swf',
    chunked: false, //分片传输
    chunkSize: 512 * 1024,
    duplicate: false,
    server : undefined,
    // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候，把图片打开。
    disableGlobalDnd: true,
    fileNumLimit: 10,
    fileSizeLimit: 50 * 1024 * 1024,    // 50 M
    fileSingleSizeLimit: 5 * 1024 * 1024,    // 5 M
    uploadSuccess:noop,

    unAllowed: 'text/plain;application/javascript',
    fileType: 'excel',
    parse:true,
    module: undefined,
    parseUrl: undefined,
    parseData:undefined,
    parseAjaxOption:{},
    content:undefined,
    tooltipOptions:{},
    urlResolver:function (url) {
      return phoenix.utils.resolve(url);
    }
  };

  var PhxUploader = function (element, options) {
    this.$container = $(element);
    this.options = $.extend({}, $.fn.phxUploader.defaults, options);
    this.init();
  };

  PhxUploader.prototype.init = function () {
    var _this = this;

    _this.initContent();

    _this.initOpts();

    _this.initUploader();
  };

  PhxUploader.prototype.initContent = function() {
    this.$container.append(this.options['content'] || this.uploaderHtml());
    this.$element = this.$container.find('.phx-uploader');
  }

  PhxUploader.prototype.initOpts = function () {

    var _this = this, $wrap = _this.$element;
    // 图片容器
    _this.$queue = $('<ul class="filelist"></ul>').appendTo($wrap.find('.uploader-queue-list'));

    // 状态栏，包括进度和控制按钮
    _this.$statusBar = $wrap.find('.uploader-status-bar');

    // 文件总体选择信息。
    _this.$info = _this.$statusBar.find('.info');

    // 上传按钮
    _this.$upload = $wrap.find('.uploader-upload-btn');

    // 没选择文件之前的内容。
    _this.$placeHolder = $wrap.find('.placeholder');

    _this.$progress = _this.$statusBar.find('.uploader-progress').hide();

    // 添加的文件数量
    _this.fileCount = 0;

    // 添加的文件总大小
    _this.fileSize = 0;

    // 优化retina, 在retina下这个值是2
    var ratio = window.devicePixelRatio || 1;

    // 缩略图大小
    _this.thumbnailWidth = 110 * ratio;
    _this.thumbnailHeight = 110 * ratio;

    // 可能有pedding, ready, uploading, confirm, done.
    _this.state = 'pedding';

    // 所有文件的进度信息，key为file id
    _this.percentages = {};

    // 判断浏览器是否支持图片的base64
    _this.isSupportBase64 = (function () {
      var data = new Image();
      var support = true;
      data.onload = data.onerror = function () {
        if (this.width != 1 || this.height != 1) {
          support = false;
        }
      }
      data.src = "";
      return support;
    })();

    _this.supportTransition = (function () {
      var s = document.createElement('p').style,
        r = 'transition' in s ||
          'WebkitTransition' in s ||
          'MozTransition' in s ||
          'msTransition' in s ||
          'OTransition' in s;
      s = null;
      return r;
    })();

    _this.docExtensions = 'doc;docx;ppt;ppt;pptx;csv;xml;jasper;jrxml;pdf;';

    _this.options['accept'] = _this.options['accept'] || _this.getFileType(_this.options['fileType'] || 'excel');
    if(!_this.options['server']) {
        _this.options['server'] = _this.resolveServer(_this.options['module']);
    }
  }

  PhxUploader.prototype.initUploader = function () {
    var _this = this;
    _this.uploader = WebUploader.create(_this.options);

    // 拖拽时不接受 js, txt 文件。
    _this.uploader.on('dndAccept', function (items) {
      var denied = false,
        len = items.length,
        i = 0;

      for (; i < len; i++) {
        // 如果在列表里面
        if (~_this.options['unAllowed'].indexOf(items[i].type)) {
          denied = true;
          break;
        }
      }

      return !denied;
    });

    // 添加“添加文件”的按钮，
    _this.uploader.addButton({
      id: '.continue-add-btn',
      label: '继续添加'
    });

    _this.uploader.on('ready', function () {
      window.uploader = _this.uploader;
    });

    _this.uploader.onUploadProgress = function (file, percentage) {
      var $li = $('#' + file.id),
        $percent = $li.find('.uploader-progress span');

      $percent.css('width', percentage * 100 + '%');
      _this.percentages[file.id][1] = percentage;
      _this.updateTotalProgress();
    };

    _this.uploader.onFileQueued = function (file) {
      _this.fileCount++;
      _this.fileSize += file.size;

      if (_this.fileCount === 1) {
        _this.$placeHolder.addClass('element-invisible');
        _this.$statusBar.show();
      }

      _this.addFile(file);
      _this.setState('ready');
      _this.updateTotalProgress();
    };

    _this.uploader.onFileDequeued = function (file) {
      _this.fileCount--;
      _this.fileSize -= file.size;

      if (!_this.fileCount) {
        _this.setState('pedding');
      }

      _this.removeFile(file);
      _this.updateTotalProgress();
    };

    _this.uploader.on('all', function (type) {
      switch (type) {
        case 'uploadFinished':
          _this.setState('confirm');
          break;

        case 'startUpload':
          _this.setState('uploading');
          break;

        case 'stopUpload':
          _this.setState('paused');
          break;
      }
    });

    _this.uploader.onUploadSuccess = function (file, response) {
      //parse excel
      var uploadSuccess = _this.options['uploadSuccess'] || function () {};
      if(_this.options['fileType']==='excel' && _this.options['parse']) {
        _this.parse(file, response, uploadSuccess);
      } else {
        uploadSuccess(file, response)
      }
    }

    _this.uploader.onError = function (code) {
      var text;
      if (code === 'F_DUPLICATE') {
        text = '文件已去重'
      } else if(code === 'Q_EXCEED_NUM_LIMIT') {
        text = '最多可选'+ _this.options['fileNumLimit'] + '张';
      } else if(code === 'Q_TYPE_DENIED') {
        text = '不支持的文件格式';
      } else if(code === 'Q_EXCEED_SIZE_LIMIT') {
        text = '文件总大小超过' + _this.options['fileSizeLimit'] /1024/1024 + 'M';
      } else if(code === 'F_EXCEED_SIZE') {
        text = '文件单张大小超过' + _this.options['fileSingleSizeLimit'] /1024/1024 + 'M';
      } else {
        text = 'Error: ' + code;
      }
      _this.$element.find('.uploader-queue-list').after('<div class="uploader-warn-message">*'+ text +'</div>');
    };

    _this.$upload.on('click', function () {
      if ($(this).hasClass('disabled')) {
        return false;
      }

      if (_this.state === 'ready') {
        _this.uploader.upload();
      } else if (state === 'paused') {
        _this.uploader.upload();
      } else if (state === 'uploading') {
        _this.uploader.stop();
      }
    });

    _this.$info.on('click', '.retry', function () {
      _this.uploader.retry();
    });

    _this.$info.on('click', '.reset', function () {
      _this.reset();
    });

    _this.$upload.addClass('state-' + _this.state);
    _this.updateTotalProgress();
  }

  PhxUploader.prototype.getFileType = function (types) {
    var extensions = [];
    var mimeTypes = [];

    if (types) {
      if(!$.isArray(types)) {
        types = [types];
      }
      $(types).each(function () {
        var _fileType = $.fn.phxUploader.fileTypes[this];
        if (_fileType) {
          extensions.push(_fileType['extensions']);
          mimeTypes.push(_fileType['mimeTypes']);
        }
      });
    }

    return {
      extensions: extensions.join(","),
      mimeTypes: mimeTypes.join(",")
    };
  }

  PhxUploader.prototype.uploaderHtml = function () {
    var _this = this;
    return ['<div class="phx-uploader-wrapper">',
        '  <div class="phx-uploader-container">',
        '    <div class="phx-uploader">',
        '<div class="uploader-queue-list">',
        '  <div class="uploader-dnd-area placeholder">',
        '    <div class="uploader-file-picker"></div>',
        '    <p>或将文件拖到这里，单次最多可选' + _this.options['fileNumLimit'] + '张</p>',
        '  </div>',
        '</div>',
        '<div class="uploader-status-bar" style="display:none;">',
        '  <div class="uploader-progress">',
        '    <span class="text">0%</span>',
        '    <span class="percentage"></span>',
        '  </div><div class="info"></div>',
        '  <div class="btns">',
        '    <div class="continue-add-btn"></div><div class="uploader-upload-btn">开始上传</div>',
        '  </div>',
        '</div>',
        '    </div>',
        '  </div>',
        '</div>'].join("");
  }

  // 当有文件添加进来时执行，负责view的创建
  PhxUploader.prototype.addFile = function (file) {
    var _this = this;
    var $li = $('<li id="' + file.id + '">' +
      '<p class="title">' + file.name + '</p>' +
      '<p class="imgWrap"></p>' +
      '<p class="uploader-progress"><span></span></p>' +
      '</li>'),

      $btns = $('<div class="file-panel"><span class="cancel">删除</span></div>').appendTo($li),
      $prgress = $li.find('p.uploader-progress span'),
      $wrap = $li.find('p.imgWrap'),
      $info = $('<p class="error"></p>'),
      text = '',
      showError = function (code) {
        switch (code) {
          case 'exceed_size':
            text = '文件大小超出';
            break;

          case 'interrupt':
            text = '上传暂停';
            break;

          default:
            text = '上传失败，请重试';
            break;
        }

        $info.text(text).appendTo($li);
      };

    if (file.getStatus() === 'invalid') {
      showError(file.statusText);
    } else {
      var defaultImage = function () {
        img = $('<span class="file-icon"></span>');
        $wrap.empty().append(img);
      }
      if (file.ext === 'xlsx'
        || file.ext === 'xls') {
        var img = $('<span class="excel-icon"></span>');
        $wrap.empty().append(img);
      } else if (~_this.docExtensions.indexOf(file.ext)) {
        defaultImage();
      } else {
        // @todo lazyload
        $wrap.text('预览中');
        uploader.makeThumb(file, function (error, src) {
          var img;

          if (error) {
            defaultImage();
            return;
          }

          if (_this.isSupportBase64) {
            img = $('<img src="' + src + '">');
            $wrap.empty().append(img);
          } else {
            defaultImage();
          }
        }, _this.thumbnailWidth, _this.thumbnailHeight);
      }

      _this.percentages[file.id] = [file.size, 0];
      file.rotation = 0;
    }

    file.on('statuschange', function (cur, prev) {
      if (prev === 'progress') {
        $prgress.hide().width(0);
      } else if (prev === 'queued') {
        $li.off('mouseenter mouseleave');
        $btns.remove();
      }

      // 成功
      if (cur === 'error' || cur === 'invalid') {
        console.log(file.statusText);
        showError(file.statusText);
        _this.percentages[file.id][1] = 1;
      } else if (cur === 'interrupt') {
        showError('interrupt');
      } else if (cur === 'queued') {
        $info.remove();
        $prgress.css('display', 'block');
        _this.percentages[file.id][1] = 0;
      } else if (cur === 'progress') {
        $info.remove();
        $prgress.css('display', 'block');
      } else if (cur === 'complete') {
        $prgress.hide().width(0);
        if(_this.options['fileType'] === 'excel') {
          $li.append('<span class="parsing">解析中</span>');
        } else {
          $li.append('<span class="success"></span>');
        }
      }

      $li.removeClass('state-' + prev).addClass('state-' + cur);
    });

    $li.on('mouseenter', function () {
      $btns.stop().animate({height: 30});
    });

    $li.on('mouseleave', function () {
      $btns.stop().animate({height: 0});
    });

    $btns.on('click', 'span', function () {
      var index = $(this).index(),
        deg;

      switch (index) {
        case 0:
          uploader.removeFile(file);
          return;
      }

      if (_this.supportTransition) {
        deg = 'rotate(' + file.rotation + 'deg)';
        $wrap.css({
          '-webkit-transform': deg,
          '-mos-transform': deg,
          '-o-transform': deg,
          'transform': deg
        });
      } else {
        $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
      }
    });

    $li.appendTo(_this.$queue);
  }

  // 负责view的销毁
  PhxUploader.prototype.removeFile = function (file) {
    var $li = $('#' + file.id);

    delete this.percentages[file.id];
    this.updateTotalProgress();
    $li.off().find('.file-panel').off().end().remove();
  }

  PhxUploader.prototype.reset = function () {
    var _this = this;
    $(_this.uploader.getFiles()).each(function () {
      _this.uploader.removeFile(this);
    });
    _this.fileCount = 0;
    _this.fileSize = 0;
    _this.$element.find('.uploader-warn-message').remove();
  }

  PhxUploader.prototype.updateTotalProgress = function () {
    var _this = this,
      loaded = 0,
      total = 0,
      spans = _this.$progress.children(),
      percent;

    $.each(_this.percentages, function (k, v) {
      total += v[0];
      loaded += v[0] * v[1];
    });

    percent = total ? loaded / total : 0;

    spans.eq(0).text(Math.round(percent * 100) + '%');
    spans.eq(1).css('width', Math.round(percent * 100) + '%');
    _this.updateStatus();
  }

  PhxUploader.prototype.updateStatus = function () {
    var _this = this, text = '', stats;

    if (_this.state === 'ready') {
      text = '选中' + _this.fileCount + '个文件，共' +
        WebUploader.formatSize(_this.fileSize) + '。<a class="reset" href="#">重置</a>';
    } else if (_this.state === 'confirm') {
      stats = _this.uploader.getStats();
      if (stats.uploadFailNum) {
        text = '已上传' + stats.successNum + ' 个，失败' +
          stats.uploadFailNum + '个，<a class="retry" href="#">重新上传</a>失败图片或<a class="reset" href="#">重置</a>'
      }
    } else {
      stats = _this.uploader.getStats();
      text = '共' + _this.fileCount + '个（' +
        WebUploader.formatSize(_this.fileSize) +
        '），已上传' + stats.successNum + '个。<a class="reset" href="#">重置</a>';

      if (stats.uploadFailNum) {
        text += '，失败' + stats.uploadFailNum + '个。<a class="reset" href="#">重置</a>';
      }
    }
    _this.$info.html(text);
  }

  PhxUploader.prototype.setState = function (val) {
    var _this = this, file, stats;

    if (val === _this.state) {
      return;
    }

    _this.$upload.removeClass('state-' + _this.state);
    _this.$upload.addClass('state-' + val);
    _this.state = val;

    switch (_this.state) {
      case 'pedding':
        _this.$placeHolder.removeClass('element-invisible');
        _this.$queue.hide();
        _this.$statusBar.addClass('element-invisible');
        _this.uploader.refresh();
        break;

      case 'ready':
        _this.$placeHolder.addClass('element-invisible');
        $('.continue-add-btn').removeClass('element-invisible');
        _this.$queue.show();
        _this.$statusBar.removeClass('element-invisible');
        _this.uploader.refresh();
        break;

      case 'uploading':
        $('.continue-add-btn').addClass('element-invisible');
        _this.$progress.show();
        _this.$upload.text('暂停上传');
        break;

      case 'paused':
        _this.$progress.show();
        _this.$upload.text('继续上传');
        break;

      case 'confirm':
        _this.$progress.hide();
        $('.continue-add-btn').removeClass('element-invisible');
        _this.$upload.text('开始上传');

        stats = _this.uploader.getStats();
        if (stats.successNum && !stats.uploadFailNum) {
          _this.setState('finish');
          return;
        }
        break;
      case 'parse':

        break;
      case 'finish':
        stats = _this.uploader.getStats();
        if (stats.successNum) {
          // finished
        } else {
          // 没有成功的图片，重设
          _this.state = 'done';
          location.reload();
        }
        break;
    }

    _this.updateStatus();
  }

  PhxUploader.prototype.resolveServer = function(module) {
    if(!module) {
      throw ("the options [server] and [module] is empty..");
    }
    var url = sprintf("{%s-control}/fileController/springUpload", module);
    return this.options['urlResolver'](url);
  }

  PhxUploader.prototype.tooltip = function ($selector, message) {
    var _this = this;
    var  tooltipOption = $.extend({}, {
      template : '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner" style="text-align:left"></div></div>',
      container: 'body',
      placement:'bottom',
      html:true,
      title: function() {
        $('.tooltip').remove();
        return message;
      }
    }, _this.tooltipOptions);
    $selector.tooltip(tooltipOption);
  }

  PhxUploader.prototype.parse = function(file, response, uploadSuccess) {
    var _this = this;
    var $li = $(sprintf('#%s', file.id));
    var ajaxOption = $.extend({}, _this.parseAjaxOption, { url:_this.options['parseUrl'] });

    if(!ajaxOption['url']) {
      if(!_this.options['module']) {
        throw ("the options [parseUrl][parseAjaxOption.url][module] are all empty.");
      }
      var url = sprintf("{%s-control}/excelController/parsingExcel.shtml", _this.options['module']);
      ajaxOption['url'] = this.options['urlResolver'](url);
    }
    if(_this.options['parseData']) {
      ajaxOption['data'] = _this.options['parseData'];
    }
    ajaxOption['data'] = $.extend({}, ajaxOption['data'],  {'fileId' : response['fileIds']});
    ajaxOption['success'] = function (result) {
      $li.find('span.parsing').remove();
      if(result) {
        if(result['success']) {
          $li.append('<span class="success"></span>');
          uploadSuccess(file, response);
        } else {
          var messages='';
          if($.isArray(result['messages'])) {
            if(result['messages'].length>=1) {
              $.each(result['messages'], function () {
                messages += this + '<br/>';
              });
              messages = messages.substring(0, messages.lastIndexOf('<br/>'));
            }
          } else {
            messages = result['messages'] || '';
          }
          var reg = new RegExp("\n","g");
          messages = messages.replace(reg,"<br/>");

          $li.append('<p class="error">解析失败，查看<a class="error-message" href="#">详情</a></p>');
          _this.tooltip($li.find('a.error-message'), messages);
        }
      } else {
        $li.append('<p class="error">解析失败</p>');
      }
    };
    ajaxOption['error'] = function(XMLHttpRequest, textStatus, errorThrown) {
      $li.find('span.parsing').remove();
      $li.append('<p class="error">解析失败，查看<a class="error-message" href="#">详情</a></p>');
      _this.tooltip($li.find('a.error-message'), textStatus);
    };

    $.ajax(ajaxOption);
  }

  PhxUploader.prototype.getUploader = function () {
    return this.uploader;
  }
}));
